package impl

import (
	"errors"
	"fmt"
	"github.com/gorilla/websocket"
	"github.com/labstack/echo/v4"
	"turingApi/conf"
	"turingApi/global"

	//"log"

	"sync"
)

type Connection struct {
	wsConnect     *websocket.Conn
	inChan        chan []byte
	outChan       chan []byte
	outStructChan chan global.ChatMessage
	closeChan     chan byte
	mutex         sync.Mutex // 对closeChan 关闭上锁
	isClosed      bool       // 防止closeChan 被关闭多次
	clientIp      string
}

func InitConnection(wsConn *websocket.Conn, c echo.Context) (conn *Connection, err error) {
	conn = &Connection{
		wsConnect:     wsConn,
		inChan:        make(chan []byte, 1000),
		outChan:       make(chan []byte, 1000),
		outStructChan: make(chan global.ChatMessage),
		closeChan:     make(chan byte, 1),
		clientIp:      c.RealIP(),
	}

	// 启动读
	go conn.readLoop()
	// 启动写
	go conn.writeLoop()

	return
}

func (conn *Connection) ReadMessage() (data []byte, err error) {
	select {
	case data = <-conn.inChan:
		conf.Logger.Info(fmt.Sprintf("User ask %s, client ip %s", string(data), conn.clientIp))
	case <-conn.closeChan:
		err = errors.New("connection closed")
	}
	return
}

func (conn *Connection) WriteStruct(data global.ChatMessage) (err error) {
	//conf.Logger.Info("model.ChatMessage ", data)
	select {
	case conn.outStructChan <- data:
		//conf.Logger.Info(fmt.Sprintf("Push response %s, client ip %s", data.Message, conn.clientIp))
	case <-conn.closeChan:
		err = errors.New("connection closed")
	}
	return
}

func (conn *Connection) WriteMessage(data []byte) (err error) {
	select {
	case conn.outChan <- data:
	case <-conn.closeChan:
		err = errors.New("connection closed")
	}
	return
}

func (conn *Connection) Close() {
	// 线程安全
	conn.wsConnect.Close()
	// 利用标记，让closeChan只关闭一次
	conn.mutex.Lock()
	if !conn.isClosed {
		conf.Logger.Info("close channel for closeChan")
		close(conn.closeChan)
		conn.isClosed = true
	}
	conn.mutex.Unlock()
}

func (conn *Connection) readLoop() {
	var (
		data []byte
		err  error
	)
	for {
		if _, data, err = conn.wsConnect.ReadMessage(); err != nil {
			conf.Logger.Info(fmt.Sprintf("Read loop failed: %v", err))
			goto ERR
		}

		select {
		case conn.inChan <- data:
		case <-conn.closeChan:
			goto ERR
		}
	}

ERR:
	conn.Close()

}

func (conn *Connection) writeLoop() {
	var (
		data []byte
		//structData model.ChatMessage
		err error
	)

	for {
		select {
		case data = <-conn.outChan:
			if err = conn.wsConnect.WriteMessage(websocket.TextMessage, data); err != nil {
				conf.Logger.Info(fmt.Sprintf("Write message failed: %v", err))
				goto ERR
			}

		case structData := <-conn.outStructChan:
			if err = conn.wsConnect.WriteJSON(structData); err != nil {
				conf.Logger.Info(fmt.Sprintf("Write json message failed: %v", err))
				goto ERR
			}
		case <-conn.closeChan:
			goto ERR
		}

	}
ERR:
	conf.Logger.Info(fmt.Sprintf("Closing conn %v", conn))
	conn.Close()

}
